﻿using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml.Linq;

/// <summary>
/// Because FBML pages cannot contain a code-behind, we try to keep the
/// C# script blocks on each page as lean as possible. Therefore, most of
/// the processing is done inside this class. You can think of this class as the
/// 'controller' for the entire application. 
/// </summary>
/// <remarks>This is the only class that should deal with Facebook and 
/// SmugMug objects simultaneously. </remarks>
public class SmfbService
{
    private SmfbDataContext context;
    private SmugMugService sms;

    /// <summary>
    /// Constructor.
    /// </summary>
	public SmfbService()
	{
        context = new SmfbDataContext();
        sms = new SmugMugService();
        sms.NickName = this.Nickname;
	}

    /// <summary>
    /// Gets the FBML to be displayed on the profile.
    /// </summary>
    public String GetProfileFbml()
    {
        StringBuilder fbml = new StringBuilder();
        fbml.Append(Resources.Profile.Header);
        List<CompositeAlbum> albums = GetCompositeAlbums();
        foreach (CompositeAlbum album in albums)
        {
            fbml.AppendFormat(Resources.Profile.Template,
                album.SmugMugImageLink, album.TinyUrl, album.Title,
                album.LastUpdated.ToShortDateString(), album.ImageCount);
        }
        fbml.Append(Resources.Profile.Footer);
        return fbml.ToString();
    }

    /// <summary>
    /// Gets the objects needed to publish a mini-feed story.
    /// </summary>
    /// <returns></returns>
    public Story GetMiniFeedStory()
    {
        Story story = new Story();
        story.Title = String.Format(Resources.Feed.MiniFeed, FacebookService.Uid);
        story.Body = null;
        foreach (CompositeAlbum album in GetCompositeAlbums())
        {
            story.Images.Add(new Facebook.Service.FeedImage(album.TinyUrl, album.SmugMugImageLink));
        }
        return story;
    }

    /// <summary>
    /// Returns true if the current user has indicated their SmugMug nickname.
    /// </summary>
    public Boolean HasNickname
    {
        get
        {
            Boolean? exists = null;
            context.HasNickname(FacebookService.Uid, ref exists);
            return (exists.HasValue) ? exists.Value : false;
        }
    }

    /// <summary>
    /// Returns the SmugMug nickname of the current user, or null if not specified.
    /// </summary>
    public String Nickname
    {
        get
        {
            String nickname = null;
            context.GetNickname(FacebookService.Uid, ref nickname);
            return nickname;
        }
    }

    /// <summary>
    /// Updates (or inserts) the requested nickname.
    /// </summary>
    /// <remarks>Expects a parameter named 'nickname' in the HttpContext.</remarks>
    public void UpdateNickname()
    {
        context.UpdateNickname(
            FacebookService.Uid, 
            HttpContext.Current.Request.Params["nickname"]);
        context.SubmitChanges();
    }

    /// <summary>
    /// Updates the featured photograph for an album.
    /// </summary>
    /// <remarks>Expects form parameters named 'album_id' and 'photo_id' in the HttpContext.</remarks>
    public void UpdateFeaturedPhoto()
    {
        Int64 albumId; Int64 photoId; 
        Int32? featuredAlbumId = null; Byte? rank = null;
        if (
            Int64.TryParse(HttpContext.Current.Request.Params["album_id"], out albumId) &&
            Int64.TryParse(HttpContext.Current.Request.Params["photo_id"], out photoId))
        {
            context.UpdateOrInsertFeaturedAlbum(ref featuredAlbumId, ref rank,
                FacebookService.Uid, albumId, photoId);
            context.SubmitChanges();

            HttpContext.Current.Response.Write(String.Format(
                "ID:{0}<br/>Rank:{1}<br/>Album:{2}<br/>Photo:{3}<br/>",
                featuredAlbumId.Value, rank.Value, albumId, photoId));
        }
    }

    /// <summary>
    /// Removes the requested featured album for this user.
    /// </summary>
    /// <remarks>Expects a parameter named 'fa' in the HttpContext.</remarks>
    public void RemoveFeaturedAlbum()
    {
        context.RemoveFeaturedAlbum(
            FacebookService.Uid,
            FeaturedAlbumIDParameter);
        context.SubmitChanges();
    }

    /// <summary>
    /// Gets a list of the currently featured albums for the user.
    /// </summary>
    /// <returns>A List of FeaturedAlbum objects.</returns>
    public List<FeaturedAlbum> GetFeaturedAlbums()
    {
        IEnumerable<FeaturedAlbum> linq =
            from item in context.FeaturedAlbums
            where item.Uid == FacebookService.Uid
            orderby item.Rank
            select item;
        return new List<FeaturedAlbum>(linq);
    }

    /// <summary>
    /// Gets a list of composite albums. First connects to the FBSM data store
    /// to return the IDs, then for each of those connects to SmugMug to get 
    /// extended data.
    /// </summary>
    /// <returns>A List of CompositeAlbum objects.</returns>
    public List<CompositeAlbum> GetCompositeAlbums()
    {
        List<CompositeAlbum> list = new List<CompositeAlbum>();
        List<FeaturedAlbum> featured = GetFeaturedAlbums();
        foreach (FeaturedAlbum baseAlbum in featured)
        {
            list.Add(MakeCompositeAlbum(baseAlbum));
        }
        return list;
    }

    /// <summary>
    /// Makes a composite album, comprised of SmugMug data and
    /// SMFB data, from a base FeaturedAlbum object.
    /// </summary>
    /// <param name="baseAlbum">The base album to start with.</param>
    /// <returns>A CompositeAlbum object.</returns>
    public CompositeAlbum MakeCompositeAlbum(FeaturedAlbum baseAlbum)
    {
        Album a = sms.GetAlbum(baseAlbum.AlbumID.ToString());
        ImageUrlSet ius = sms.GetImageUrlSet(baseAlbum.PhotoID.ToString());

        return new CompositeAlbum
        {
            FeaturedAlbumId = baseAlbum.ID,
            Rank = baseAlbum.Rank,
            AlbumId = baseAlbum.AlbumID.ToString(),
            ImageId = baseAlbum.PhotoID.ToString(),
            Title = a.Title,
            Description = a.Description,
            Category = a.CategoryName,
            LastUpdated = a.LastUpdated,
            ImageCount = a.ImageCount,
            TinyUrl = ius.TinyUrl,
            ThumbUrl = ius.ThumbUrl,
            Nickname = sms.NickName
        };
    }

    /// <summary>
    /// Gets a list of categories <b>in use</b> by the current user.
    /// </summary>
    /// <returns>A List of Category objects.</returns>
    /// <remarks>This will only return SmugMug categories that have one
    /// or more albums assigned to them.</remarks>
    public List<Category> GetUsedCategories()
    {
        List<Category> categories = sms.GetUsedCategories();
        foreach (Category cat in categories)
        {
            if (cat.ID == HttpContext.Current.Request.Params["c"])
            {
                cat.Selected = "selected";
            }
        }
        return categories;
    }

    /// <summary>
    /// Gets a list of albums based on the current query parameters.
    /// </summary>
    /// <returns>A List of Album objects.</returns>
    /// <remarks>
    /// If a parameter 'k' is specified, then a keyword match is performed.
    /// If a parameter 'c' is specified, then a category match is performed.
    /// </remarks>
    public List<Album> FindAlbums()
    {
        String k = KeywordParameter;
        String c = CategoryIDParameter;
        IEnumerable<Album> linq =
            from a in sms.GetAlbums()
            where
            (
                (String.IsNullOrEmpty(k) || a.Title.Contains(k))
                &&
                (String.IsNullOrEmpty(c) || a.CategoryID.Equals(c))
            )
            orderby a.Title
            select a;
        return new List<Album>(linq);
    }

    /// <summary>
    /// Gets a textual description of the filters that have been applied.
    /// </summary>
    /// <returns>A string such as " in the Family category containing 'baby'".</returns>
    public String GetAlbumFilterDescription()
    {
        String k = KeywordParameter;
        String c = CategoryIDParameter;
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        if (!String.IsNullOrEmpty(c)) sb.AppendFormat(" in the {0} category", c);
        if (!String.IsNullOrEmpty(k)) sb.AppendFormat(" containing '{0}'", k);
        return sb.ToString();
    }

    /// <summary>
    /// Gets the currently selected album.
    /// </summary>
    /// <returns>An Album object.</returns>
    /// <remarks>Requires the 'a' parameter to have been passed.</remarks>
    public Album GetAlbum()
    {
        return sms.GetAlbum(AlbumIDParameter);
    }

    /// <summary>
    /// Gets all of the photos associated with the selected album.
    /// </summary>
    /// <returns>A List of ImageUrlSets.</returns>
    /// <remarks>Requires the 'a' parameter to have been passed.</remarks>
    public List<ImageUrlSet> GetPhotos()
    {
        return sms.GetImageUrlSets(AlbumIDParameter);
    }

    /// <summary>
    /// Gets the value of the 'k' parameter.
    /// </summary>
    public String KeywordParameter { get { return HttpContext.Current.Request.Params["k"]; } }
    
    /// <summary>
    /// Gets the value of the 'c' parameter.
    /// </summary>
    public String CategoryIDParameter { get { return HttpContext.Current.Request.Params["c"]; } }
    
    /// <summary>
    /// Gets the value of the 'a' parameter.
    /// </summary>
    public String AlbumIDParameter { get { return HttpContext.Current.Request.Params["a"]; } }

    /// <summary>
    /// Gets the value of the 'fa' parameter.
    /// </summary>
    public Byte? FeaturedAlbumIDParameter
    {
        get
        {
            Byte p;
            if (Byte.TryParse(HttpContext.Current.Request.Params["fa"], out p)) return p;
            else return null;
        }
    }
}
